跨資料庫支援 × 自動升級 × 一致性設計

在 ERP 系統中,資料表結構常隨著需求變動而頻繁調整,且往往需要同時支援 SQL Server/MySQL/PostgreSQL/Oracle 等多種資料庫。
若直接在程式中使用各資料庫的原生型別(如 NVARCHAR、NUMBER 等),一旦要異動欄位型別或調整長度,就必須修改所有 SQL 定義與程式碼,維護與遷移成本極高。
BeeNET 採用抽象資料型別(Abstract Data Type)的概念,以抽象的 FieldDbType 列舉來定義資料欄位,再由 DbTable 作為資料表結構定義物件。
部署時交由比對工具與命令產生器轉換為各資料庫對應的 DDL / DML 語法,達成跨資料庫支援與自動升級。
String、Text、DateTime 等直觀型別,由框架自動轉換。varchar/nvarchar 等差異。抽象資料型別以列舉表示,統一由 DbTable 欄位的 DbType 屬性參照:
/// <summary>
/// 欄位的抽象資料型別(跨資料庫對應)。
/// </summary>
public enum FieldDbType
{
    String,        // 一般字串(具長度)
    Text,          // 大量文字
    Boolean,       // 布林值
    AutoIncrement, // 自動遞增整數(資料表主鍵常用)
    Short,         // 16-bit 整數
    Integer,       // 32-bit 整數
    Long,          // 64-bit 整數
    Decimal,       // 十進位數值(高精度)
    Currency,      // 金額(預設 19,4)
    Date,          // 日期
    DateTime,      // 日期時間
    Guid,          // 全域唯一識別碼
    Binary         // 二進位資料
}
| FieldDbType | SQL Server | MySQL | PostgreSQL | Oracle | 
|---|---|---|---|---|
| String | NVARCHAR(n) | VARCHAR(n) | VARCHAR(n) | VARCHAR2(n) | 
| Text | NVARCHAR(MAX) | TEXT | TEXT | CLOB | 
| Boolean | BIT | TINYINT(1) / BOOLEAN | BOOLEAN | NUMBER(1) | 
| AutoIncrement | INT IDENTITY(1,1) | INT AUTO_INCREMENT | SERIAL / INT GENERATED ALWAYS AS IDENTITY | NUMBER + SEQUENCE | 
| Short | SMALLINT | SMALLINT | SMALLINT | NUMBER(5) | 
| Integer | INT | INT | INTEGER | NUMBER(10) | 
| Long | BIGINT | BIGINT | BIGINT | NUMBER(19) | 
| Decimal | DECIMAL(p,s) | DECIMAL(p,s) | NUMERIC(p,s) | NUMBER(p,s) | 
| Currency | DECIMAL(19,4) | DECIMAL(19,4) | NUMERIC(19,4) | NUMBER(19,4) | 
| Date | DATE | DATE | DATE | DATE | 
| DateTime | DATETIME | DATETIME | TIMESTAMP | TIMESTAMP | 
| Guid | UNIQUEIDENTIFIER | CHAR(36) / BINARY(16) | UUID | RAW(16) | 
| Binary | VARBINARY(MAX) | BLOB | BYTEA | BLOB | 
FieldDbType 宣告欄位結構與限制(長度、精度、是否必填)。範例:使用 TableSchemaBuilder 建立或更新資料表
// 載入指定 tableName 的 DbTable 定義,執行資料結構比對與更新
var builder = new TableSchemaBuilder(databaseId); 
builder.Execute(dbName, tableName); 
此機制可於部署階段自動比對結構並同步升級。
DbTable 物件會序列化為 XML 格式,存放於專案中以供版本控制與部署使用。每個資料表的結構定義皆以 XML 檔({TableName}.DbTable.xml)保存,確保開發、測試與正式環境的結構自動保持一致。
以 st_user 資料表為例,示範 DbTable 定義方式:
<?xml version="1.0" encoding="utf-8"?>
<DbTable DbName="common" TableName="st_user" DisplayName="用戶">
  <Fields>
    <DbField FieldName="sys_no" Caption="流水號" DbType="AutoIncrement" />
    <DbField FieldName="sys_rowid" Caption="唯一識別" DbType="Guid" />
    <DbField FieldName="sys_id" Caption="用戶帳號" DbType="String" Length="20" />
    <DbField FieldName="sys_name" Caption="用戶名稱" DbType="String" Length="20" />
    <DbField FieldName="password" Caption="登入密碼" DbType="String" Length="40" />
    <DbField FieldName="email" Caption="電子郵件" DbType="String" Length="100" />
    <DbField FieldName="note" Caption="備註" DbType="String" Length="200" />
    <DbField FieldName="sys_insert_time" DisplayName="寫入時間" DbType="DateTime" />
  </Fields>
  <Indexes>
    <DbTableIndex Name="pk_{0}" Unique="true" PrimaryKey="true">
      <IndexFields>
        <IndexField FieldName="sys_no" />
      </IndexFields>
    </DbTableIndex>
    <DbTableIndex Name="rx_{0}" Unique="true">
      <IndexFields>
        <IndexField FieldName="sys_rowid" />
      </IndexFields>
    </DbTableIndex>
    <DbTableIndex Name="ux_{0}" Unique="true">
      <IndexFields>
        <IndexField FieldName="sys_id" />
      </IndexFields>
    </DbTableIndex>
  </Indexes>
</DbTable>
抽象資料型別讓資料結構設計更具一致性與延展性,
DbTable、TableSchemaComparer 與 TableSchemaBuilder 共同構成了 BeeNET 在資料結構層的自動化核心。
透過這樣的架構,ERP 系統能在多資料庫與持續演進的需求下,
仍保持結構一致、部署順暢,並有效降低長期維護成本。
FieldDbType 的設計使開發人員能專注於商業邏輯與流程創新,
而不必被資料庫型別與結構差異所綁架,真正實現高效與穩定並行的開發模式。
📘 HackMD 原文筆記:
👉 https://hackmd.io/@jeff377/field-dbtype-design
📢 歡迎轉載,請註明出處
📬 歡迎追蹤我的技術筆記與實戰經驗分享
Facebook | HackMD | GitHub | NuGet